package com.t4.process.service.impl;



import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.t4.auth.service.SysUserService;
import com.t4.model.process.Process;
import com.t4.model.process.ProcessRecord;
import com.t4.model.process.ProcessTemplate;
import com.t4.model.process.ProcessType;
import com.t4.model.system.SysUser;
import com.t4.process.mapper.ProcessMapper;
import com.t4.process.service.ProcessRecordService;
import com.t4.process.service.ProcessService;
import com.t4.process.service.ProcessTemplateService;
import com.t4.process.service.ProcessTypeService;
import com.t4.security.custom.LoginUserInfoHelper;
import com.t4.vo.process.ApprovalVo;
import com.t4.vo.process.ProcessFormVo;
import com.t4.vo.process.ProcessQueryVo;
import com.t4.vo.process.ProcessVo;
import com.t4.wechat.service.WechatMessageService;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

@Service
public class ProcessServiceImpl extends ServiceImpl<ProcessMapper, Process> implements ProcessService {

    @Resource
    private ProcessMapper processMapper;
    
    @Resource
    private ProcessTemplateService processTemplateService;

    @Resource
    private ProcessTypeService processTypeService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private ProcessRecordService processRecordService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;

    @Resource
    private WechatMessageService wechatMessageService;

    @Override
    public IPage<ProcessVo> selectPage(Page<ProcessVo> pageParam, ProcessQueryVo processQueryVo) {
        IPage<ProcessVo> page = processMapper.selectPage(pageParam, processQueryVo);
        return page;
    }

    @Override
    public void deployByZip(String deployPath) {
        // 定义zip输入流
        InputStream inputStream = this
                .getClass()
                .getClassLoader()
                .getResourceAsStream(deployPath);
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);

        // 流程部署
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .deploy();
    }

    @Override
    public void startUp(ProcessFormVo processFormVo) {
        // 1. 根据档期那用户id获取用户信息
        Long userId = LoginUserInfoHelper.getUserId();
        SysUser sysUser = sysUserService.getById(userId);
        // 2. 根据审批模板id把模板信息查询出来
        Long processTemplateId = processFormVo.getProcessTemplateId();
        ProcessTemplate processTemplate = processTemplateService.getById(processTemplateId);
        // 3. 保存提交的审批信息到业务表（oa_process）
        Process process = new Process();
        String processCode = System.currentTimeMillis() + "";
        process.setProcessCode(processCode);
        process.setUserId(userId);
        process.setProcessTypeId(processFormVo.getProcessTypeId());
        process.setProcessTemplateID(processTemplateId);
        process.setTitle(sysUser.getName() + "发起" + processTemplate.getName() + "申请");
        String formValues = processFormVo.getFormValues();
        process.setFormValues(formValues);
        process.setStatus(1);
        baseMapper.insert(process);
        // 4. 启动流程实例（RuntimeService）
        // 4.1 流程定义key
        String processDefinitionKey = processTemplate.getProcessDefinitionKey();
        // 4.2 业务key processId
        String businessKey = String.valueOf(process.getId());
        // 4.3 流程参数form表单json数据，转map集合
        JSONObject jsonObject = JSONObject.parseObject(formValues);
        JSONObject formData = jsonObject.getJSONObject("formData");
        Map<String, Object> map = new HashMap<>();
        for(Map.Entry<String, Object> entry: formData.entrySet()){
            map.put(entry.getKey(), entry.getValue());
        }
        // 4.4 启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
        String processInstanceId = processInstance.getId();
        process.setProcessInstanceId(processInstanceId);
        // 5. 查询下一个审批人
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        // 可能下一个审批人同时有多个（并行审批）
        if(!CollectionUtils.isEmpty(taskList)) {
            List<String> assigneeList = new ArrayList<>();
            for (Task task : taskList) {
                SysUser user = sysUserService.getUserByUsername(task.getAssignee());
                assigneeList.add(user.getName());
                // 6. 推送消息给下一个审批人
                wechatMessageService.pushPendingMessage(process.getId(), user.getId(), task.getId());
            }
            String currentAuditor = StringUtils.join(assigneeList.toArray(), ",");
            process.setCurrentAuditor(currentAuditor);
            process.setDescription("等待" + currentAuditor + "审批");
        }
        // 7. 业务和流程关联，更新oa_process数据
        this.updateById(process);
        // 8. 记录操作行为
        processRecordService.record(process.getId(), 1, "发起申请");
    }

    @Override
    public IPage<ProcessVo> findPending(Page<Process> pageParam) {
        // 1. 封装查询条件
        String username = LoginUserInfoHelper.getUsername();
        TaskQuery taskQuery = taskService.createTaskQuery().taskAssignee(username)
                .orderByTaskCreateTime().desc();
        // 2. 分页查询
        List<Task> taskList = taskQuery.listPage(
                (int)((pageParam.getCurrent()-1)*pageParam.getSize()), // 开始位置
                (int)pageParam.getSize()); // 每页显示记录数
        long totalCount = taskQuery.count();
        // 3. 封装返回list集合数据
        List<ProcessVo> processVoList = new ArrayList<>();
        for(Task task: taskList){
            String processInstanceId = task.getProcessInstanceId();
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            if(processInstance == null){
                continue;
            }
            Long businessKey = Long.valueOf(processInstance.getBusinessKey());
            if(businessKey == null){
                continue;
            }
            Process process = baseMapper.selectById(businessKey);
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            SysUser sysUser = sysUserService.getById(process.getUserId());
            processVo.setName(sysUser.getUsername());
            ProcessType processType = processTypeService.getById(process.getProcessTypeId());
            processVo.setProcessTypeName(processType.getName());
            ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateID());
            processVo.setProcessTemplateName(processTemplate.getName());
            processVo.setTaskId(task.getId());
            processVoList.add(processVo);
        }
        // 4. 封装返回IPage对象
        IPage<ProcessVo> page = new Page<>(
                pageParam.getCurrent(),
                pageParam.getSize(),
                totalCount);
        page.setRecords(processVoList);
        return page;
    }

    @Override
    public Map<String, Object> show(Long id) {
        // 1. 根据流程id获取流程信息Process
        Process process = baseMapper.selectById(id);
        // 2. 根据流程id获取流程记录信息ProcessRecord
        List<ProcessRecord> processRecordList = processRecordService.list(new LambdaQueryWrapper<ProcessRecord>()
                .eq(ProcessRecord::getProcessId, id));
        // 3. 根据模板id获取模板信息
        ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateID());
        // 4. 判断当前用户是否可以审批，以及不能重复审批
        boolean isApprove = false;
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(process.getProcessInstanceId()).list();
        if(!CollectionUtils.isEmpty(taskList)){
            // 当前流程实例中的当前任务可能会有多个人审批（并行审批）
            for(Task task: taskList){
                if(task.getAssignee().equals(LoginUserInfoHelper.getUsername())){
                    isApprove = true;
                }
            }
        }
        // 5. 查询数据封装到map集合，返回
        Map<String, Object> map = new HashMap<>();
        map.put("process", process);
        map.put("processRecordList", processRecordList);
        map.put("processTemplate", processTemplate);
        map.put("isApprove", isApprove);
        return map;
    }

    @Override
    public void approve(ApprovalVo approvalVo) {
        String taskId = approvalVo.getTaskId();
        // 1.  根据任务id获取流程变量
        Map<String, Object> variables = taskService.getVariables(taskId);
        // 2. 判断审批状态值
        if(approvalVo.getStatus() == 1){
            // 2.1 状态值=1，审批通过
            taskService.complete(taskId);
        }else {
            // 2.2 状态值=-1，驳回，流程结束
            this.endTask(taskId);
        }
        // 3. 记录审批相关过程信息
        String description = approvalVo.getStatus().intValue() == 1? "已通过": "驳回";
        processRecordService.record(approvalVo.getProcessId(), approvalVo.getStatus(), description);
        // 4. 查询下一个审批人，并更新Process记录
        Process process = baseMapper.selectById(approvalVo.getProcessId());
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(process.getProcessInstanceId()).list();
        // 4.1 存在后续审批
        if(!CollectionUtils.isEmpty(taskList)){
            List<String> assigneeList = new ArrayList<>();
            for(Task task: taskList){
                String assignee = task.getAssignee();
                SysUser sysUser = sysUserService.getUserByUsername(assignee);
                assigneeList.add(sysUser.getName());
                // 推送消息给下一个审批人
                wechatMessageService.pushPendingMessage(process.getId(), sysUser.getId(), task.getId());
            }
            String currentAuditor = StringUtils.join(assigneeList.toArray(), ",");
            process.setCurrentAuditor(currentAuditor);
            process.setDescription("等待" + currentAuditor + "审批");
            process.setStatus(1);
        }else{
            // 4.2 不存在后续审批
            if(approvalVo.getStatus().intValue() == 1){
                process.setDescription("审批完成（同意）");
                process.setStatus(2);
            }else{
                process.setDescription("审批完成（拒绝）");
                process.setStatus(-1);
            }
            process.setCurrentAuditor(null);
        }
        // 4.3 更新Process
        baseMapper.updateById(process);
        // 5. 推送消息给申请人

    }

    private void endTask(String taskId) {
        // 1. 根据任务id获取任务对象 Task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 2. 获取流程定义模型 BpmnModel
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        // 3. 获取结束流向节点
        List<EndEvent> endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
        if (CollectionUtils.isEmpty(endEventList)){
            return;
        }
        FlowNode endFlowNode = (FlowNode) endEventList.get(0);
        // 4. 获取当前流向节点
        FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
        // 5. 临时保存当前活动原始方向
        List originalSequenceFlowList = new ArrayList<>();
        originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
        // 6. 清理当前流动方向
        currentFlowNode.getOutgoingFlows().clear();
        // 7. 创建新流向
        SequenceFlow newSequenceFlow = new SequenceFlow();
        newSequenceFlow.setId("newSequenceFlowId");
        newSequenceFlow.setSourceFlowElement(currentFlowNode);
        newSequenceFlow.setTargetFlowElement(endFlowNode);
        // 8. 当前节点指向新方向
        List newSequenceFlowList = new ArrayList<>();
        newSequenceFlowList.add(newSequenceFlow);
        currentFlowNode.setOutgoingFlows(newSequenceFlowList);
        // 9. 完成当前任务
        taskService.complete(task.getId());
    }

    @Override
    public IPage<ProcessVo> findProcessed(Page<Process> pageParam) {
        // 1. 封装查询条件
        HistoricTaskInstanceQuery historicTaskInstanceQuery = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(LoginUserInfoHelper.getUsername())
                .finished().orderByTaskCreateTime().desc();
        long totalCount = historicTaskInstanceQuery.count();
        // 2. 条件分页查询
        List<HistoricTaskInstance> historicTaskInstanceList = historicTaskInstanceQuery.
                listPage((int) ((pageParam.getCurrent() - 1) * pageParam.getSize()), (int) pageParam.getSize());
        // 3. 遍历集合，封装List<ProcessVo>
        List<ProcessVo> processVoList = new ArrayList<>();
        for(HistoricTaskInstance historicTaskInstance: historicTaskInstanceList){
            String businessKey = historicTaskInstance.getBusinessKey();
            if(businessKey == null){
                continue;
            }
            Process process = baseMapper.selectById(Long.valueOf(businessKey));
            ProcessVo processVo = new ProcessVo();
            BeanUtils.copyProperties(process, processVo);
            SysUser sysUser = sysUserService.getById(process.getUserId());
            processVo.setName(sysUser.getUsername());
            ProcessType processType = processTypeService.getById(process.getProcessTypeId());
            processVo.setProcessTypeName(processType.getName());
            ProcessTemplate processTemplate = processTemplateService.getById(process.getProcessTemplateID());
            processVo.setProcessTemplateName(processTemplate.getName());
            processVoList.add(processVo);
        }
        // 4. IPage封装分页查询所有数据
        IPage<ProcessVo> page = new Page<>(pageParam.getCurrent(),
                pageParam.getSize(),
                totalCount);
        page.setRecords(processVoList);
        return page;
    }

    @Override
    public IPage<ProcessVo> findStarted(Page<ProcessVo> pageParam) {
        ProcessQueryVo processQueryVo = new ProcessQueryVo();
        processQueryVo.setUserId(LoginUserInfoHelper.getUserId());
        IPage<ProcessVo> processVoIPage = baseMapper.selectPage(pageParam, processQueryVo);
        return processVoIPage;
    }


}
